package com.example.basic.base.impl;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.enums.SqlMethod;
import com.baomidou.mybatisplus.core.toolkit.Assert;
import com.baomidou.mybatisplus.core.toolkit.ReflectionKit;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
import com.example.basic.base.BaseEntityMapper;
import com.example.basic.base.BaseEntityService;
import com.example.basic.base.IdEntity;
import com.example.basic.base.MySqlMethod;
import com.example.basic.utils.IdUtils;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.ibatis.binding.MapperMethod;
import org.apache.ibatis.session.SqlSession;
import org.apache.logging.log4j.util.Strings;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

import java.util.Collection;
import java.util.Iterator;
import java.util.List;

public abstract class BaseEntityServiceImpl<M extends BaseEntityMapper<T>, T extends IdEntity> extends BaseServiceImpl implements BaseEntityService<T> {
    @Autowired
    protected M mapper;

    @Override
    public Integer insert(T entity) {
        // 生成ID
        entity.setId(IdUtils.fastUUID());
        return mapper.insert(entity);
    }

    @Override
    public Integer updateById(T entity) {
        return this.mapper.updateById(entity);
    }

    @Override
    public Integer update(T entity, Wrapper<T> updateWrapper) {
        return this.mapper.update(entity, updateWrapper);
    }

    @Override
    public Integer save(T entity) {
        if (entity.getId() != null && Strings.isNotEmpty(entity.getId())) {
            return updateById(entity);
        } else {
            return insert(entity);
        }
    }

    @Override
    public T selectById(String id) {
        return this.mapper.selectById(id);
    }

    @Override
    public Integer deleteById(String id) {
        return this.mapper.deleteById(id);
    }

    @Override
    public Integer deleteBatchIds(List<String> ids) {
        return this.mapper.deleteBatchIds(ids);
    }

    @Override
    public Integer delete(T entity) {
        if(entity == null){
            return -1;
        }
        return this.mapper.delete(new QueryWrapper<>(entity));
    }

    @Override
    public List<T> selectAll(T entity) {
        return this.mapper.selectList(new QueryWrapper<>(entity));
    }

    @Override
    public Page<T> selectPage(Page<T> page) {
        page.setRecords(this.mapper.selectPage(page, null).getRecords());
        return page;
    }
    @Override
    public List<T> selectBatchIds(List<String> idList) {
        return this.mapper.selectBatchIds(idList);
    }

    @Override
    public T selectOne(T condition) {
        List<T> list = this.mapper.selectList(new QueryWrapper<>(condition));
        if (CollectionUtils.isNotEmpty(list)) {
            return list.get(0);
        }
        return null;
    }

    @Override
    public boolean isValidId(String id) {
        return id != null;
    }

    @Override
    public boolean isValidEntityId(IdEntity idEntity) {
        return idEntity != null && isValidId(idEntity.getId());
    }

    @Transactional(
            rollbackFor = {Exception.class}
    )
    public boolean saveBatch(Collection<T> entityList) {
        String sqlStatement = this.sqlStatement(SqlMethod.INSERT_ONE);
        SqlSession batchSqlSession = this.sqlSessionBatch();
        Throwable var5 = null;

        try {
            int i = 0;

            for(Iterator var7 = entityList.iterator(); var7.hasNext(); ++i) {
                T anEntityList = (T) var7.next();
                batchSqlSession.insert(sqlStatement, anEntityList);
                if (i >= 1 && i % 1000 == 0) {
                    batchSqlSession.flushStatements();
                }
            }

            batchSqlSession.flushStatements();
            return true;
        } catch (Throwable var16) {
            var5 = var16;
            throw var16;
        } finally {
            if (batchSqlSession != null) {
                if (var5 != null) {
                    try {
                        batchSqlSession.close();
                    } catch (Throwable var15) {
                        var5.addSuppressed(var15);
                    }
                } else {
                    batchSqlSession.close();
                }
            }

        }
    }

    protected String sqlStatement(SqlMethod sqlMethod) {
        return SqlHelper.table(this.currentModelClass()).getSqlStatement(sqlMethod.getMethod());
    }

    protected Class<T> currentModelClass() {
        return (Class<T>) ReflectionKit.getSuperClassGenericType(this.getClass(), 1);
    }

    protected SqlSession sqlSessionBatch() {
        return SqlHelper.sqlSessionBatch(this.currentModelClass());
    }

    protected String mySqlStatement(MySqlMethod mySqlMethod) {
        return SqlHelper.table(this.currentModelClass()).getSqlStatement(mySqlMethod.getMethod());
    }

    @Transactional(
            rollbackFor = {Exception.class}
    )
    public boolean updateBatchById(Collection<T> entityList) {
        Assert.notEmpty(entityList, "error: entityList must not be empty", new Object[0]);
        String sqlStatement = this.mySqlStatement(MySqlMethod.UPDATE_BY_ID);
        SqlSession batchSqlSession = this.sqlSessionBatch();
        Throwable var5 = null;

        try {
            int i = 0;

            for(Iterator var7 = entityList.iterator(); var7.hasNext(); ++i) {
                T anEntityList = (T) var7.next();
                MapperMethod.ParamMap<T> param = new MapperMethod.ParamMap();
                param.put("et", anEntityList);
                batchSqlSession.update(sqlStatement, param);
                if (i >= 1 && i % 1000 == 0) {
                    batchSqlSession.flushStatements();
                }
            }

            batchSqlSession.flushStatements();
            return true;
        } catch (Throwable var17) {
            var5 = var17;
            throw var17;
        } finally {
            if (batchSqlSession != null) {
                if (var5 != null) {
                    try {
                        batchSqlSession.close();
                    } catch (Throwable var16) {
                        var5.addSuppressed(var16);
                    }
                } else {
                    batchSqlSession.close();
                }
            }

        }
    }
}
